using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace CoreCalc.SheetDefinedFunctions
{
    public class CellAddressStringConverter
    {
        private StringBuilder result;
        private Regex a1Format;
        private int c;
        private int r;

        //TODO: if row or column are 0 then does the internal representation exclude the 0? If so we need to do the same here
        public CellAddressStringConverter()
        {
            a1Format = new Regex("\\u0024?[A-Z]+\\u0024?[1-9][0-9]*");
        }

        public string ConvertStringContainingA1RefsToC0R0(string a1, int col, int row)
        {
            return ConvertStringContainingA1Refs(a1, col, row, new MatchEvaluator(ConvertSingleA1FormatRefToC0R0));
        }

        public string ConvertStringContainingA1RefsToR1C1(string a1, int col, int row)
        {
            return ConvertStringContainingA1Refs(a1, col, row, new MatchEvaluator(ConvertSingleA1FormatRefToR1C1));
        }

        private string ConvertStringContainingA1Refs(string a1, int col, int row, MatchEvaluator eval)
        {
            this.c = col;
            this.r = row;
            result = new StringBuilder();

            char[] delims = { '"' };
            //removal of empty strings fucks up stuff like =="" and such
            string[] splits = a1.Split(delims);

            if (a1.StartsWith("\"")) AddPartialString(0, splits, a1Format, eval);
            else FindA1Refs(0, splits, a1Format, eval);
            return result.ToString();
        }

        private void FindA1Refs(int i, string[] splits, Regex row, MatchEvaluator m)
        {
            if (i >= splits.Length) return;
            result.Append(row.Replace(splits[i], m));
            AddPartialString(i + 1, splits, row, m);
        }

        private void AddPartialString(int i, string[] splits, Regex r, MatchEvaluator m)
        {
            if (i >= splits.Length) return;
            result.Append("\"" + splits[i] + "\""); //Add the removed quotation marks
            FindA1Refs(i + 1, splits, r, m);
        }

        private string ConvertSingleA1FormatRefToC0R0(Match m)
        {
            return ConvertSingleA1FormatRefToC0R0(m.Value, c, r);
        }

        public string ConvertSingleA1FormatRefToC0R0(string s, int cc, int rr)
        {
            string colComponent = Regex.Match(s, "\\u0024?[A-Z]+").Value;
            string rowComponent = Regex.Match(s, "\\u0024?[1-9][0-9]*").Value;
            StringBuilder back = new StringBuilder();

            RowColComponentResult rc = A1ColumnToC0R0Column(colComponent, cc);
            if (rc.abs) back.Append("C" + rc.colRow);
            else back.Append("C[" + rc.colRow + "]");

            if ((rowComponent).StartsWith("$"))
            {
                back.Append("R" + (int.Parse(rowComponent.Remove(0, 1)) - 1));
            }
            else back.Append("R[" + (int.Parse(rowComponent) - 1 - rr) + "]");

            return back.ToString();
        }

        private string ConvertSingleA1FormatRefToR1C1(Match m)
        {
            return ConvertSingleA1FormatRefToR1C1(m.Value, c, r);
        }

        public string ConvertSingleA1FormatRefToR1C1(string s, int cc, int rr)
        {
            string colComponent = Regex.Match(s, "\\u0024?[A-Z]+").Value;
            string rowComponent = Regex.Match(s, "\\u0024?[1-9][0-9]*").Value;
            StringBuilder back = new StringBuilder();

            if ((rowComponent).StartsWith("$"))
            {
                back.Append("R" + (int.Parse(rowComponent.Remove(0, 1))));
            }
            else back.Append("R[" + (int.Parse(rowComponent) - 1 - rr) + "]");

            RowColComponentResult rc = A1ColumnToC0R0Column(colComponent, cc);
            if (rc.abs) back.Append("C" + (rc.colRow + 1));
            else back.Append("C[" + rc.colRow + "]");

            return back.ToString();
        }

        public static int[] GetColAndRowNumberFromA1(string s, int currentCol, int currentRow)
        {
            int colComponent = 0;
            RowColComponentResult ss = A1ColumnToC0R0Column(Regex.Match(s, "\\u0024?[A-Z]+").Value, currentCol);
            colComponent = ss.colRow;

            int rowComponent = 0;
            string row = Regex.Match(s, "\\u0024?[1-9][0-9]*").Value;
            if (row.StartsWith("$")) rowComponent = int.Parse(row.Substring(1)) - 1;
            else rowComponent = int.Parse(row) - 1 - currentRow;

            return new int[] { colComponent, rowComponent };
        }

        private static RowColComponentResult A1ColumnToC0R0Column(string s, int currentCol)
        {
            bool absolute = false;
            int numberBack = 0;
            if (s.StartsWith("$")) { s = s.Remove(0, 1); absolute = true; currentCol = 0; } //currentCol == 0 to cancel the effect of subtraction

            for (int i = 0; i < s.Length; i++)
            {
                numberBack += (int)((((s[i] - 65) + 1) * Math.Pow(26, s.Length - i - 1)));
                if (i == s.Length - 1) numberBack -= 1;
            }
            return new RowColComponentResult((numberBack - currentCol), absolute);
        }
    }

    struct RowColComponentResult
    {
        public readonly int colRow;
        public readonly bool abs;

        public RowColComponentResult(int colRow, bool abs)
        {
            this.colRow = colRow;
            this.abs = abs;
        }
    }
}
